package Mediadores;

import GUI.GUIPrincipal;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.util.LinkedList;

import GUI.GUIPrincipal;
import Metadatos.Metadatos;
import Conexion.Conexion;

public class MediadorPrincipal implements ActionListener{

	private GUIPrincipal guiPrincipal = null;

	public MediadorPrincipal() throws Exception{
		guiPrincipal = new GUIPrincipal();
		guiPrincipal.setActionListeners(this);
	}

	/** 
	 * Invoca acciones dependiendo del boton que ha sido accionado
	 * @param e representa un evento, que en este caso seria accionar un boton
	 */
	public void actionPerformed(ActionEvent e) { 

		Object source = e.getSource();

		if (source == guiPrincipal.getBotonCerrar()){
			this.presionBotonCerrar();
		}
		if (source == guiPrincipal.getBotonComparar()){
			this.presionBotonComparar();
		}
	}

	private void presionBotonCerrar() {
		guiPrincipal.dispose();
	}

	private void presionBotonComparar() {
		Conexion.comenzarConexion();
		Connection conexion1 = Conexion.getConexion1();
		Connection conexion2 = Conexion.getConexion2();
		String[] infoTriggers = new String[4];
		String muestTriggers;
		infoTriggers = mostrarTrigger(conexion1, conexion2);
		muestTriggers = infoTriggers[0] + infoTriggers[1] + infoTriggers[2] + infoTriggers[3]; 
		String info;
		info = mostrarTablas(conexion1,conexion2) + compararProcedures(conexion1,conexion2) +"\n" +muestTriggers;
		guiPrincipal.getAreaInforme().setText(info);
		guiPrincipal.getAreaInforme().setCaretPosition(0);
	}
	
	public String mostrarTablas(Connection conexion1, Connection conexion2){
		LinkedList <String> tablasBD1 = Metadatos.getNombresTablas(conexion1, null,null);
		LinkedList <String> tablasBD2 = Metadatos.getNombresTablas(conexion2, null,null);
		LinkedList<String[]> colsBD1;
		LinkedList<String[]> colsBD2;
		LinkedList<String[]> fkBD1 = new LinkedList<String[]>();
		LinkedList<String[]> fkBD2 = new LinkedList<String[]>();
		
		String tablasExcCon1 = "Tablas Exclusivas BD " + Conexion.getBDConexion1() + "\n-----------------------------------\n    ";
		String tablasExcCon2 = "Tablas Exclusivas BD " + Conexion.getBDConexion2() + "\n-----------------------------------\n    ";
		String tablasMismoNombre = "Tablas diferentes estructura en BD " +Conexion.getBDConexion1()+" y " +Conexion.getBDConexion2()+"\n---------------------------------------\n    ";
		String tablasIguales = "Tablas iguales en BD "+Conexion.getBDConexion1()+" y " +Conexion.getBDConexion2()+"\n---------------------------------------\n    ";
		String tablaActual;
		String clavesForaneas ="";
		boolean flag = true;
		for(int i = 0; i<tablasBD1.size();i++){
			tablaActual = tablasBD1.get(i);
			flag = true;
			for(int j = 0; j<tablasBD2.size();j++){
				if (tablaActual.compareTo(tablasBD2.get(j))==0){
					tablasBD2.remove(tablaActual);
					flag = false;
					colsBD1 = Metadatos.getDatosColumnas(conexion1, tablaActual, null, Conexion.getSchema1());
					colsBD2 = Metadatos.getDatosColumnas(conexion2, tablaActual, null, Conexion.getSchema2());
					fkBD1 = Metadatos.getClavesForaneas(conexion1, tablaActual, null, Conexion.getSchema1());
					fkBD2 = Metadatos.getClavesForaneas(conexion2, tablaActual, null, Conexion.getSchema2());
					String[] auxCompararColumnas = compararColumnas(colsBD1,colsBD2);
					if(auxCompararColumnas[4].compareTo("iguales")==0  && compararFK(fkBD1,fkBD2)[2].compareTo("iguales")==0){ 
						tablasIguales += tablaActual + "\n    ";
					}else{
						tablasMismoNombre += "TABLA    " + tablaActual + "\n";
						tablasMismoNombre += "    Columnas exclusivas BD" +Conexion.getBDConexion1()+":\n        " + auxCompararColumnas[0]; 
						tablasMismoNombre += "\n    Columnas exclusivas BD" + Conexion.getBDConexion2()+":\n        " +auxCompararColumnas[1];
						tablasMismoNombre += "\n    Columnas iguales en ambas BD:\n        " + auxCompararColumnas[2];
						tablasMismoNombre += "\n    Columnas diferentes BD "+Conexion.getBDConexion1() + " || " + Conexion.getBDConexion2() +"\n        " + auxCompararColumnas[3]+"\n";
						String[] fks = compararFK(fkBD1,fkBD2);
						clavesForaneas += "\nClaves foraneas exclusivas BD "+Conexion.getBDConexion1()+"\n-------------------------------------------\n    (1)Nombre || (2)->tabla || (3)->columna || (4)fklocal || (5)On Update || (6)On Delete || (7)Tabla Local\n\n"+
								fks[0] +"Claves foraneas exclusivas BD "+Conexion.getBDConexion2()+"\n-------------------------------------------\n    (1)Nombre || (2)->tabla || (3)->columna || (4)fklocal || (5)On Update || (6)On Delete || (7)Tabla Local\n\n"+
								fks[1]+"Claves foraneas iguales en tablas con mismo nombre\n----------------------------------\n"+fks[3]+"\n";
					}
				}
			}
			if (flag){
				tablasExcCon1 += tablaActual + "\n    ";
			}
		}
		
		for (int i = 0; i<tablasBD2.size(); i++){
			tablasExcCon2 += tablasBD2.get(i) +"\n    ";
		}
		return tablasExcCon1 + "\n" + tablasExcCon2 + "\n" + tablasMismoNombre + clavesForaneas+"\n" + tablasIguales + "\n";
	}
	
	private String mostrarAccion(String accion){
		String resultado = "";
		Integer key;
		key = Integer.parseInt(accion);
		switch(key){
		case DatabaseMetaData.importedKeyCascade : resultado = "Cascade";break;
		case DatabaseMetaData.importedKeyNoAction : resultado = "No Action";break;
		case DatabaseMetaData.importedKeyRestrict : resultado = "Resctrict";break;
		case DatabaseMetaData.importedKeySetDefault : resultado = "Set Default";break;
		case DatabaseMetaData.importedKeySetNull : resultado = "Set NULL";break;
		}
		return resultado;
	}
	
	private String[] compararColumnas(LinkedList<String[]> columnas1, LinkedList<String[]> columnas2){
		String[] diferencias = new String[5];
		String dif = "";
		String iguales = "";
		String excCol1 = "";
		String excCol2 = "";
		boolean columEncontrada = true;
		String columActual;
		for (int i = 0; i<columnas1.size(); i++){
			columActual = columnas1.get(i)[0];
			for(int j = 0; j<columnas2.size(); j++){
				columEncontrada = false;
				if (columActual.compareTo(columnas2.get(j)[0])==0){
					if(columnas1.get(i)[1].compareTo(columnas2.get(j)[1])!= 0){
						dif += mostrarInfoColumna(columnas1.get(i)) + "	||	" + mostrarInfoColumna(columnas2.get(j)) + "\n        ";
					}else{
						if ((columnas1.get(i)[2].compareTo(columnas2.get(j)[2])) != 0){
							dif += mostrarInfoColumna(columnas1.get(i)) + "	||	" + mostrarInfoColumna(columnas2.get(j)) + "\n        "; 
						}else{
						//	if(columnas1.get(i)[3].compareTo(columnas2.get(j)[3]) != 0){
					//			dif += mostrarInfoColumna(columnas1.get(i)) + "	||	" + mostrarInfoColumna(columnas2.get(j)) + "\n        ";
					//		}else{
								iguales += mostrarInfoColumna(columnas1.get(i)) + "\n        ";
					//		}
						}
					}
					columEncontrada = true;
					columnas2.remove(columnas2.get(j));
					break;
				}
			}
			if (!columEncontrada){
				excCol1 += mostrarInfoColumna(columnas1.get(i)) + "\n        ";
			}
		}
		
		for (int i = 0; i<columnas2.size(); i++){
			excCol2 += mostrarInfoColumna(columnas2.get(i)) + "\n        ";
		}
		diferencias[0] = excCol1;
		diferencias[1] = excCol2;
		diferencias[2] = iguales;
		diferencias[3] = dif;
		if(excCol1.length() == 0 && excCol2.length() == 0 && dif.length() == 0)
			diferencias[4] = "iguales";
		else
			diferencias[4] = "distintas";
//		System.out.println("Exclusivas 1: \n" + excCol1 + "\nExc 2\n"+ excCol2 + "\niguales\n" +iguales+"\ndiferentes\n" + dif);
		for (int i=0;i<diferencias.length;i++) {
			if (diferencias[i].length()==0) {
				diferencias[i] = "    No hay datos\n\n";
			}
		}
		return diferencias;
	}
	
	private boolean compararArreglos(String[] a, String[] b) {
		boolean res = true;
		int i=0;
		while(i<a.length && a[i].equals(b[i])) {
			i++;
		}
		return i==a.length;
	}
	
	private String[] compararFK(LinkedList<String[]> foraneas1,LinkedList<String[]> foraneas2){
		String[] resultado = new String[4];
		LinkedList<String[]> clavesIgualesLista = new LinkedList<String[]>();
		LinkedList<String[]> clavesExcl1Lista = new LinkedList<String[]>();
		LinkedList<String[]> clavesExcl2Lista = new LinkedList<String[]>();
		String clavesIguales="";
		String clavesExcl1="";
		String clavesExcl2="";
		String iguales = "iguales";
		for (int i = 0; i<foraneas1.size(); i++){
			for (int j = 0; j<foraneas2.size();j++){
				if (compararArreglos(foraneas1.get(i), foraneas2.get(j))) {
					if (!clavesIgualesLista.contains(foraneas1.get(i))) {
						clavesIgualesLista.add(foraneas1.get(i));	
					}
				}else{
					iguales="Diferentes";
					if (!clavesExcl1Lista.contains(foraneas1.get(i))) {
						clavesExcl1Lista.add(foraneas1.get(i));
					}
					if(!clavesExcl2Lista.contains(foraneas2.get(j))) {
						clavesExcl2Lista.add(foraneas2.get(j));
					}
				}		
			}
		}
		for (int i=0;i<clavesIgualesLista.size();i++) {
			clavesIguales += "    (1) "+clavesIgualesLista.get(i)[0]+"\n    (2) "+clavesIgualesLista.get(i)[1]+"\n    (3) "+clavesIgualesLista.get(i)[2]+"\n    (4) "+clavesIgualesLista.get(i)[3]+"\n    (5) "+mostrarAccion(clavesIgualesLista.get(i)[4])+"\n    (6) "+mostrarAccion(clavesIgualesLista.get(i)[5])+"\n    (7) "+clavesIgualesLista.get(i)[6]+"\n\n";
		}
		for (int i=0;i<clavesExcl1Lista.size();i++) {
			if (!contieneArreglo(clavesExcl2Lista,clavesExcl1Lista.get(i))) {
				clavesExcl1 += "    (1) "+clavesExcl1Lista.get(i)[0]+"\n    (2) "+clavesExcl1Lista.get(i)[1]+"\n    (3) "+clavesExcl1Lista.get(i)[2]+"\n    (4) "+clavesExcl1Lista.get(i)[3]+"\n    (5) "+mostrarAccion(clavesExcl1Lista.get(i)[4])+"\n    (6) "+mostrarAccion(clavesExcl1Lista.get(i)[5])+"\n    (7) "+clavesExcl1Lista.get(i)[6]+"\n\n";
			}
		}
		for (int i=0;i<clavesExcl2Lista.size();i++) {
			if (!contieneArreglo(clavesExcl1Lista,clavesExcl2Lista.get(i))) {
				clavesExcl2 += "    (1) "+clavesExcl2Lista.get(i)[0]+"\n    (2) "+clavesExcl2Lista.get(i)[1]+"\n    (3) "+clavesExcl2Lista.get(i)[2]+"\n    (4) "+clavesExcl2Lista.get(i)[3]+"\n    (5) "+mostrarAccion(clavesExcl2Lista.get(i)[4])+"\n    (6) "+mostrarAccion(clavesExcl2Lista.get(i)[5])+"\n    (7) "+clavesExcl2Lista.get(i)[6]+"\n\n";
			}
		}
		resultado[3] = clavesIguales;
		resultado[0] = clavesExcl1;
		resultado[1] = clavesExcl2;
		resultado[2] = iguales;
		for (int i=0;i<resultado.length;i++) {
			if (resultado[i].length()==0) {
				resultado[i] = "    No hay datos\n\n";
			}
		}
		return resultado;
	}
	
	private boolean contieneArreglo(LinkedList<String[]> a, String[] b){
		int i=0;
		while (i<a.size() && !compararArreglos(a.get(i), b)) {
			i++;
		}
		return i<a.size();
	}
	
//	private boolean compararPK(LinkedList<String> claves1, LinkedList<String> claves2){
//		boolean resultado = true;
//		boolean claveEncontrada = true;
//		String claveActual;
//		if (claves1.size() != claves2.size())
//			resultado = false;
//		for (int i = 0; i<claves1.size() && resultado; i++){
//			claveActual = claves1.get(i);
//			claveEncontrada = false;
//			for (int j = 0; j<claves2.size();j++){
//				if (claveActual.compareTo(claves2.get(j))==0){
//					claveEncontrada = true;
//					break;
//				}
//			}
//			resultado = resultado && claveEncontrada;
//		}
//		return resultado;
//	}
	
//	private boolean compararIndices(LinkedList<String> indice1, LinkedList<String> indice2){
//		boolean resultado = true;
//		boolean indiceEncontrado = true;
//		String indiceActual;
//		if (indice1.size() != indice2.size())
//			resultado = false;
//		for (int i = 0; i<indice1.size() && resultado; i++){
//			indiceActual = indice1.get(i);
//			indiceEncontrado = false;
//			for (int j = 0; j<indice2.size();j++){
//				if (indiceActual.compareTo(indice2.get(j))==0){
//					indiceEncontrado = true;
//					break;
//				}
//			}
//			resultado = resultado && indiceEncontrado;
//		}
//		return resultado;
//	}

	
	private String mostrarInfoColumna(String[] columna){
		String resultado ="";
		resultado += columna[0] + " " + columna[1] + "("+ columna[2] +")" /*+ " " + columna[3]*/;
		return resultado;
	}
	
	public String[] mostrarTrigger(Connection conexion1,Connection conexion2){
		String nombre = "";//,accion,tabla,condicion = "";
		LinkedList <String[]> Tabla1 = Metadatos.nombresYCondTriggers(conexion1,Conexion.getSchema1());
		LinkedList <String[]> Tabla2 = Metadatos.nombresYCondTriggers(conexion2,Conexion.getSchema2());		
		String[] salida= new String[4];
		String triggersTab1 = "\n"+"Triggers exlusivos de la tabla " + Conexion.getSchema1() + " Base de datos " + Conexion.getBDConexion1() + "\n----------------------------------------\n";
		String triggersTab2 = "\n"+"Triggers exlusivas de la tabla " + Conexion.getSchema2() + " Base de datos " + Conexion.getBDConexion2() + "\n----------------------------------------\n";
		String triggersTabID = "\n"+"Triggers de ambas tablas iguales, pero con comportamientos diferentes" + "\n----------------------------------------\n";
		String triggersTabI = "\n"+"Triggers iguales" + "\n----------------------------------------\n";
		boolean flag;
		for(int i=0;i<Tabla1.size();i++){
			nombre = Tabla1.get(i)[0];
			flag = true;
			for(int j=0;j<Tabla2.size();j++){
				if(nombre.compareTo(Tabla2.get(j)[0]) == 0){
					flag = false;
					if( (((Tabla1.get(i)[1]).compareTo(Tabla2.get(j)[1]))==0) && ((Tabla1.get(i)[3]).compareTo(Tabla2.get(j)[3]))==0 ){
						triggersTabI = triggersTabI + nombre +" \n";
						Tabla2.remove(Tabla2.get(j));
					}else{
						triggersTabID = triggersTabID + "Tabla : " +Conexion.getSchema1()+"  "+Tabla1.get(i)[0]+"  "+Tabla1.get(i)[1]+"  "+Tabla1.get(i)[3] + "\n" +"Tabla : " +Conexion.getSchema2()+"  "+Tabla2.get(j)[0]+"  "+Tabla2.get(j)[1]+"  "+Tabla2.get(j)[3]+" \n";
						Tabla2.remove(Tabla2.get(j));
					}
				}
			}
			if(flag){
				triggersTab1 = triggersTab1 + nombre+" \n";
			}			
		}
		for(int h=0;h<Tabla2.size();h++){
			triggersTab2 = triggersTab2 + Tabla2.get(h)[0]+" \n";
		}
		salida[0] = triggersTab1;
		salida[1] = triggersTab2;
		salida[2] = triggersTabI;
		salida[3] = triggersTabID;
		return salida;
	}
	
	private String compararProcedures(Connection conexion1, Connection conexion2){
		
		LinkedList<String> nombresProcCon1 = Metadatos.getNombresProcedimientos(conexion1, Conexion.getSchema1()); 
		LinkedList<String> nombresProcCon2 = Metadatos.getNombresProcedimientos(conexion2, Conexion.getSchema2());
		String procCon1 = "\nProcedimientos propios de la BD " + Conexion.getBDConexion1() + "\n------------------------------------------------\n    ";
		String procCon2 = "\nProcedimientos propios de la BD " + Conexion.getBDConexion2() + "\n------------------------------------------------\n    ";
		String procIguales = "\nProcedimientos iguales en ambas BD (" + Conexion.getBDConexion1() +" y " +Conexion.getBDConexion2()+")\n------------------------------------------------\n    " ;
		String diferenciaParametros = "\nProcedimientos iguales pero con distinto perfil \n------------------------------------------------\n    ";
		String procActual = "";
		Boolean flag;
		for (int i = 0;i<nombresProcCon1.size();i++){
			procActual = nombresProcCon1.get(i);
			flag = false;
			for (int j = 0;j<nombresProcCon2.size();j++){
				if (procActual.compareTo(nombresProcCon2.get(j)) == 0){
					nombresProcCon2.remove(procActual);
					flag = true;
					LinkedList<String[]> param1 = Metadatos.getParametrosProcedimiento(conexion1, Conexion.getSchema1(), procActual);
					LinkedList<String[]> param2 = Metadatos.getParametrosProcedimiento(conexion2, Conexion.getSchema2(), procActual);
					boolean banderaParametros = true;
					for(int k = 0; k<param1.size();k++){
						if (param1.get(k)[0].compareTo(param2.get(k)[0])!=0 || param1.get(k)[1].compareTo(param2.get(k)[1])!=0 ||
							param1.get(k)[2].compareTo(param2.get(k)[2])!=0)
							banderaParametros = false;
					}
					if (banderaParametros){
						procIguales += procActual + " " + mostrarParametros(param1)+" \n    ";
					}else{
						diferenciaParametros += "DB: " +Conexion.getBDConexion1() +"\n    ";
						diferenciaParametros += "    Procedimiento: " + procActual + " ";
						diferenciaParametros += mostrarParametros(param1);
						diferenciaParametros += "    DB: " +Conexion.getBDConexion2()+"\n    ";
						diferenciaParametros += "    Procedimiento: " + procActual + " ";
						diferenciaParametros += mostrarParametros(param2)+"\n----\n    ";
					}
				}
			}
			if (!flag){
				procCon1 += procActual + ".\n    ";
			}
		}
		
		for (int i = 0;i<nombresProcCon2.size();i++){
			procCon2 += nombresProcCon2.get(i)+ "\n    ";
		}
		return procCon1 + procCon2 + procIguales + diferenciaParametros;
	}

	private String mostrarParametros(LinkedList<String[]> listaParam){
		String resultado = " (";
		int key;
		for (int i = 1; i<listaParam.size();i++){
			if (i != 1)
				resultado += ", ";
			key = Integer.parseInt(listaParam.get(i)[1]);
			switch (key) {
				case 1: resultado += "IN ";break;
				case 2: resultado += "IN ";break;
				case 3: resultado += "INOUT ";break;
				case 4: resultado += "OUT ";break;
				case 5: resultado += "return value ";break;
				case 6: resultado += "ResultSet ";break;
				default:break;
			}
			resultado += listaParam.get(i)[0] + " ";
			resultado += listaParam.get(i)[2] + " ";
		}
		resultado += ") ";
		resultado += "returns " + listaParam.get(0)[2] + "\n";
		return resultado;
	}
	
	
}








